﻿using Common.FrontEnd;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Util.Props;

namespace FrontEnd.Filter
{
    /// <summary>
    /// Implements a high-pass filter that compensates for attenuation in the audio data. Speech signals have an attenuation
    /// (a decrease in intensity of a signal) of 20 dB/dec. It increases the relative magnitude of the higher frequencies
    /// with respect to the lower frequencies.
    ///
    /// The Preemphasizer takes a object that usually represents audio data as input, and outputs the same 
    /// object, but with preemphasis applied. For each value X[i] in the input Data object X, the following formula is
    /// applied to obtain the output Data object Y:
    ///     
    /// <code> Y[i] = X[i] - (X[i-1]/preemphasisFactor) </code>
    /// 
    /// where 'i' denotes time.
    /// 
    /// The preemphasis factor has a value defined by the field {@link #PROP_PREEMPHASIS_FACTOR} of 0.97. A common value for
    /// this factor is something around 0.97.
    /// 
    /// Other objects are passed along unchanged through this Preemphasizer.
    /// <p/>
    /// The Preemphasizer emphasizes the high frequency components, because they usually contain much less energy than lower
    /// frequency components, even though they are still important for speech recognition. It is a high-pass filter because
    /// it allows the high frequency components to "pass through", while weakening or filtering out the low frequency
    /// components.
    /// </summary>
    public class Preemphasizer : BaseDataProcessor
    {
        /** The property for preemphasis factor/alpha. */
        [S4Double(defaultValue = 0.97)]
        public static String PROP_PREEMPHASIS_FACTOR = "factor";

        private double preemphasisFactor;
        private double prior;

        public Preemphasizer( double preemphasisFactor ) 
        {
            this.preemphasisFactor = preemphasisFactor;
        }

        public Preemphasizer( ) 
        {

        }

        override
        public void newProperties(PropertySheet ps)
        {
            base.newProperties(ps);
            preemphasisFactor = ps.getDouble(PROP_PREEMPHASIS_FACTOR);
        }

        /// <summary>
        /// Returns the next Data object being processed by this Preemphasizer, or if it is a Signal, it is returned without
        /// modification.
        /// </summary>
        /// <returns>the next available Data object, returns null if no Data object is available</returns>
        override public IData getData() 
        {
            IData input = getPredecessor().getData();
            getTimer().start();
            if (input != null) {
                if (input is DoubleData) 
                {
                    applyPreemphasis(((DoubleData) input).Values);
                    //for (int i = 0; i < ((DoubleData)input).dimension(); i++)
                    //    Trace.WriteLine(((DoubleData)input).Values[i]);
                    //Trace.Write(input.ToString());
                } 
                else if (input is DataEndSignal || input is SpeechEndSignal) 
                {
                    prior = 0;
                }
            }
            getTimer().stop();
            return input;
        }


        /// <summary>
        /// Applies pre-emphasis filter to the given Audio. The preemphasis is applied in place.
        /// </summary>
        /// <param name="_in">audio data</param>
        private void applyPreemphasis(double[] _in) 
        {
            // set the prior value for the next Audio
            double nextPrior = prior;
            if (_in.Length > 0) 
            {
                nextPrior = _in[_in.Length - 1];
            }
            if (_in.Length > 1 && preemphasisFactor != 0.0) 
            {
                // do preemphasis
                double current;
                double previous = _in[0];
                _in[0] = previous - preemphasisFactor* prior;
                for (int i = 1; i < _in.Length; i++) 
                {
                    current = _in[i];
                    _in[i] = current - preemphasisFactor* previous;
                    previous = current;
                }
            }
            prior = nextPrior;
        }
    }
}
